﻿using System;
using System.Runtime.InteropServices;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Cache;
using System.IO;
using System.ComponentModel;
using System.Text.RegularExpressions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Reflection;

namespace Webcoda.SP.WP {
    [Guid("81d870d6-80a1-4b5e-a461-624201d94110")]
    [Serializable, XmlRoot("WebcodaRssRotator")]
    public class RssRotator : Microsoft.SharePoint.WebPartPages.WebPart, System.Web.UI.ICallbackEventHandler {
        private Panel uiPnlRotatorContainer = new Panel();
        private Literal uiLitDynamicMessage = new Literal();
        private Repeater uiRptDisplay = new Repeater();
        private XmlDataSource uiXmlDataSource = new XmlDataSource();
        private XmlDocument _RssXMLDocument = new XmlDocument();
        private HtmlGenericControl divContainer = new HtmlGenericControl("div");
        private SyndicationFeed _SyndicationFeed = new SyndicationFeed();
        private StringBuilder _ErrorMessageLog = new StringBuilder();
        private string _EventArgument = string.Empty;

        #region Properties
        private string _RssFeedUrl = "http://blog.sharepointsydney.com.au/category/syndication.axd";
        [Category("Configuration")]
        [WebBrowsable(false)]
        [WebDescription("Please enter the URL for the Rss feed.")]
        [WebDisplayName("Rss Feed URL")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("SourceURL")]
        public string PA_RssFeedUrl {
            get {
                return this._RssFeedUrl;
            }
            set {
                this._RssFeedUrl = value;
            }
        }

        private int _TotalFeedCount = 5;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter the number of feed items to import.")]
        [WebDisplayName("Number of feed items to import")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("ItemsToImport")]
        public int PB_TotalFeedCount {
            get
            {
                return this._TotalFeedCount;
            }
            set
            {
                this._TotalFeedCount = value;
            }
        }

        private int _NoItemsDisplay = 1;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter number of feed items to display.")]
        [WebDisplayName("Number of feed items to display")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("ItemsToDisplay")]
        public int PC_NoItemsDisplay {
            get {
                return this._NoItemsDisplay;
            }
            set {
                this._NoItemsDisplay = value;
            }
        }

        private int _DescriptionSize = 140;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter the number of characters of main story to display.")]
        [WebDisplayName("Number of characters in story")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("DescriptionSize")]
        public int PD_DescriptionSize {
            get {
                return this._DescriptionSize;
            }
            set {
                this._DescriptionSize = value;
            }
        }

        private bool _LinkInNewWindow = false;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please select open link in new window.")]
        [WebDisplayName("Open link in new window")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("LinkInNewWindow")]
        public bool PE_LinkInNewWindow {
            get {
                return this._LinkInNewWindow;
            }
            set {
                this._LinkInNewWindow = value;
            }
        }

        private bool _LinkRelativeURL = false;
        [Category("Configuration")]
        [WebBrowsable(false)]
        [WebDescription("Please select if internal (ie /Pages/listfeed.aspx?List=%7B...) vs external (ie www.smh.com.au). Provides AAM support.")]
        [WebDisplayName("URL is internal (points to Sharepoint list)")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("LinkRelativeURL")]
        public bool PEE_LinkRelativeURL {
            get {
                return this._LinkRelativeURL;
            }
            set {
                this._LinkRelativeURL = value;
            }
        }  

        private bool _DisplayDate = false;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please select to display item date.")]
        [WebDisplayName("Display date")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("DisplayDate")]
        public bool PF_DisplayDate {
            get {
                return this._DisplayDate;
            }
            set {
                this._DisplayDate = value;
            }
        }

        private bool _DisplayHeaderLink = false;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please select to make title a link.")]
        [WebDisplayName("Display title link")]
        [Personalizable(PersonalizationScope.User)]
        [XmlElement("DisplayHeaderLink")]
        public bool PFD_DisplayHeaderLink {
            get {
                return this._DisplayHeaderLink;
            }
            set {
                this._DisplayHeaderLink = value;
            }
        }

        public enum EffectOptions {
            Cover,
            Fade,
            Scroll_Horz,
            Scroll_Vert,
            Scroll_Up,
            Scroll_Down,
            Scroll_Left,
            Scroll_Right,
            Uncover
        }
        private EffectOptions _Effect = EffectOptions.Scroll_Up;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter transition effects.")]
        [WebDisplayName("Rotation Effect")]
        [Personalizable(PersonalizationScope.User)]
        [XmlIgnore]
        public EffectOptions PG_Effect {
            get {
                return this._Effect;
            }
            set {
                this._Effect = value;
            }
        }

        private int _DelayTimeMsec = 0;
        private int _DelayTime = 0;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter initial delay (seconds).")]
        [WebDisplayName("Initial delay (seconds)")]
        [Personalizable(PersonalizationScope.User)]
        [XmlIgnore]
        public int PH_DelayTime {
            get {
                return this._DelayTime;
            }
            set {
                this._DelayTime = value;
            }
        }

        private int _TransitionTimeMsec = 4000;
        private int _TransitionTime = 4;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter transitions delay (seconds).")]
        [WebDisplayName("Transition delay (seconds)")]
        [Personalizable(PersonalizationScope.User)]
        [XmlIgnore]
        public int PI_TransitionTime {
            get {
                return this._TransitionTime;
            }
            set {
                this._TransitionTime = value;
            }
        }

        private int _TransitionSpeedMsec = 2000;
        private int _TransitionSpeed = 1;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter transitions speed (seconds).")]
        [WebDisplayName("Transition speed (seconds)")]
        [Personalizable(PersonalizationScope.User)]
        [XmlIgnore]
        public int PJ_TransitionSpeed {
            get {
                return this._TransitionSpeed;
            }
            set {
                this._TransitionSpeed = value;
            }
        }

        private int _DataCache = 0;
        [Category("Configuration")]
        [WebBrowsable(true)]
        [WebDescription("Please enter duration to cache Rss Data (seconds).")]
        [WebDisplayName("Rss cache duration (seconds)")]
        [Personalizable(PersonalizationScope.User)]
        [XmlIgnore]
        public int PK_DataCache {
            get {
                return this._DataCache;
            }
            set {
                this._DataCache = value;
            }
        }

        #region Custom XSLT Properties

        private string _Xsl = string.Empty;
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        [XmlIgnore]
        [HtmlDesigner("", HtmlEditorBuilderType = "string")]
        public string Xsl {
            get {
                return _Xsl;
            }
            set {
                _Xsl = value;
            }
        }

        private string _Xml = string.Empty;
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        [XmlIgnore]
        public string Xml {
            get {
                return this._Xml;
            }
            set {
                this._Xml = value;
            }
        }

        private string _Rss = "";
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        [XmlElement("RSSFeed")]
        public string RSS {
            get {
                return this._Rss;
            }
            set {
                _Rss = value;
            }
        }

        //private string _ItemCssClass = "rssrotatoritem";
        //[WebBrowsable(false)]
        //[Personalizable(PersonalizationScope.Shared)]
        //[XmlElement("RSSFeed")]
        //public string ItemCssClass {
        //    get {
        //        return this._ItemCssClass;
        //    }
        //    set {
        //        _ItemCssClass = value;
        //    }
        //}

        #endregion
        #endregion Properties

        #region Webpart Methods
        public RssRotator() {
            ExportMode = WebPartExportMode.All;
        }

        /// <summary>
        /// 1. Oninit fires before page load. Modifications to the page that are necessary to support Ajax are done here.
        /// Then load the Properties of this webpart
        /// </summary>
        protected override void OnInit(EventArgs e) {
            Page.Init += InitPage;
            base.OnInit(e);
        }

        /// <summary>
        /// Init Page
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e_Init"></param>
        private void InitPage(object sender, EventArgs e_Init) {

        }

        /// <summary>
        /// 2. On Load Page Event (after Page_Load event of container default.aspx)
        /// </summary>
        /// <param name="e"></param>
        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);            
        }
        
        /// <summary>
        /// 3. Create Controls
        /// </summary>
        protected override void CreateChildControls() {
            try {
                //EnsureScriptManager();

                // Load Controls
                this.LoadControls();

                // Register loading scripts Scripts
                RegisterLoadScripts();

                //Register Scripts
                this.RegisterScripts();

                // Create Controls
                base.CreateChildControls();               
            } catch (Exception ex) {
                this._ErrorMessageLog.AppendLine(ex.Message);
            }
        }       

        /// <summary>
        /// 4. Render overwrite the controls on the page
        /// </summary>
        /// <param name="writer"></param>
        protected override void RenderContents(HtmlTextWriter writer) {
            try {
                if (this._ErrorMessageLog.Length > 0) {
                    DisplayErrorMessage(writer);
                    return;
                }

                // Display Container                
                uiPnlRotatorContainer.RenderControl(writer);

            } catch (Exception ex) {
                this._ErrorMessageLog.AppendLine(ex.Message);
            }
        }

        /// <summary>
        ///	Gets the custom tool parts for this Web Part by
        ///	overriding the GetToolParts method of the WebPart
        ///	base class. You must implement custom tool parts in
        ///	a separate class that derives from
        ///	Microsoft.SharePoint.WebPartPages.ToolPart.
        ///	</summary>
        ///<returns>
        /// An array of references to ToolPart objects.
        ///</returns>
        public override ToolPart[] GetToolParts() {
            ToolPart[] toolparts = new ToolPart[4];
            WebPartToolPart wptp = new WebPartToolPart();
            CustomPropertyToolPart custom = new CustomPropertyToolPart();
            toolparts[0] = custom;
            toolparts[1] = wptp;
            
            Toolparts.PickerToolPart picker = new Toolparts.PickerToolPart();
            picker.Title = "RSS Source";
            picker.Items.Add(new Toolparts.PickerToolPart.PickerItem("Internal RSS Source", "PA_RssFeedUrl", Toolparts.PickerToolPart.PickerType.List));
            toolparts[2] = picker;

            Toolparts.XslToolPart xslToolPart = new Webcoda.SP.WP.Toolparts.XslToolPart();
            toolparts[3] = xslToolPart;

            return toolparts;
        }


       /// <summary>
        /// Returns the results of a callback event that targets a control. 
       /// </summary>
       /// <param name="eventArgument"></param>
        public void RaiseCallbackEvent(string eventArgument) {
            // Processes a callback event on the server using the event
            // argument from the client.
            this._EventArgument = eventArgument;

            //Simulate the call taking some time to return
            System.Threading.Thread.Sleep(500);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public string GetCallbackResult() {
            // Write the Output to Client
            StringWriter sWriter = new StringWriter();
            HtmlTextWriter writer = new HtmlTextWriter(sWriter);

            // Returns the results of a callback event to the client.
            if (this._EventArgument == "init") {

                try {
                    // Load Controls
                    this.LoadControls();

                    // Set the Rss Feed
                    this.DatabindControls();

                    // Set Webpart Settings on callback - doesn't work bc not writing properties to database
                    //this.SetWebpartSetting();

                } catch (Exception ex) {
                    this._ErrorMessageLog.AppendLine(ex.Message);

                    // Need to display beause this is part of the Ajax
                    this.uiLitDynamicMessage.Text = "<span style=\"color:Red;\">Error loading webpart: " + this._ErrorMessageLog.Replace(System.Environment.NewLine, "<br />").ToString() + "</span>";
                    this.uiPnlRotatorContainer.Controls.Add(uiLitDynamicMessage);
                }

                // Return the Result
                uiPnlRotatorContainer.RenderControl(writer);
            }

            this._EventArgument = string.Empty;
            return string.Format("{0}¤{1}", sWriter.ToString().Replace("&amp;#39;", "\'"), this._Xml);

        }
        #endregion

        #region Private Methodes
        /// <summary>
        /// Register Loading Scripts
        /// </summary>
        private void RegisterLoadScripts() {

            //ClientScriptManager pageScriptManager = this.Page.ClientScript;
            ClientScriptManager pageScriptManager = this.Page.ClientScript;
            if (!pageScriptManager.IsClientScriptIncludeRegistered("WCCycleRssFeedLoad_" + this.ClientID)) {
                StringBuilder scriptLoading = new StringBuilder();
                scriptLoading.AppendLine("$(document).ready(function() {")
                          .AppendLine("         " + Page.ClientScript.GetCallbackEventReference(this, "'init'", "HandleRssRotatorCallbackResult_" + this.ClientID, "'" + string.Format("{0}¤uiLitRawXml", this.uiPnlRotatorContainer.ClientID) + "'", true))
                          .AppendLine("   });");

                //HandleCallbackResult will catch the string returned
                //  The JavaScript function does not have to be called HandleCallbackResult
                //arg will contain the return string from GetCallbackResult
                //context will be the id of the client-side element
                //  to update with the return string
                scriptLoading.AppendLine("function HandleRssRotatorCallbackResult_" + this.ClientID + "(arg, context){")
                          .AppendLine("  try{")
                          .AppendLine("    var elements = context.split('¤');")
                          .AppendLine("    var args = arg.split('¤');")
                          //.AppendLine("    alert('Elements[0] = ' + elements[0]);alert('Elements[1] = ' + elements[1]);alert('Args[0] = ' + args[0]);alert('Args[1] = ' + args[1]);")
                          .AppendLine("    var rotator = document.getElementById(elements[0]); ")
                          .AppendLine("    if(rotator){ ")
                          .AppendLine("      rotator.parentNode.innerHTML = args[0]; ")
                          .AppendLine("      " + GetScriptRotator())
                          .AppendLine("    } ")
                          .AppendLine("    var toolpartXml = document.getElementById(elements[1]); ")
                          .AppendLine("    if(toolpartXml){ ")
                          //escape xml using jQuery
                          .AppendLine("      toolpartXml.innerHTML = $('<div/>').text(args[1]).html();")
                          .AppendLine("    } ")
                          .AppendLine("  } ")
                          .AppendLine("  catch(e){")
                          .AppendLine("     alert('Error on callback: ' + e + ' ' + e.Message);")
                          .AppendLine("     window.status = 'Error on WebcodaRssRotator callback: ' + e.Message;")
                          .AppendLine("  } ")
                          .AppendLine("} ");
                pageScriptManager.RegisterStartupScript(this.GetType(), "WCCycleRssFeedLoad_" + this.ClientID, scriptLoading.ToString(), true);
            }
        }

        /// <summary>
        /// Register Scripts
        /// </summary>
        private void RegisterScripts() {
            // Register the Scrips 
            ClientScriptManager pageScriptManager = this.Page.ClientScript;
            if (!pageScriptManager.IsClientScriptIncludeRegistered("rssrotator.js")) {
                pageScriptManager.RegisterClientScriptInclude(this.GetType(), "rssrotator.js", "/_layouts/webcoda.rssrotator/rssrotator.js");
                //pageScriptManager.RegisterForEventValidation(this.uiPnlRotatorContainer.ClientID);
            }            

            // Register Style Sheet
            HtmlHead head = (HtmlHead)Page.Header;
            HtmlLink cssLink = new HtmlLink();
            cssLink.Attributes.Add("rel", "stylesheet");
            cssLink.Attributes.Add("type", "text/css");
            cssLink.Href = "/_layouts/webcoda.rssrotator/rssrotator.css";
            head.Controls.Add(cssLink);
        }

        /// <summary>
        /// Rotator Script
        /// </summary>
        private string GetScriptRotator() {
            // run this code on page load
            this._DelayTimeMsec = this._DelayTime * 1000;
            this._TransitionTimeMsec = this._TransitionTime * 1000;
            this._TransitionSpeedMsec = this._TransitionSpeed * 1000;
            return "WCCycleRssFeed('" + this.uiPnlRotatorContainer.ClientID + "', " + this._DelayTimeMsec.ToString() + ", " + this._TransitionTimeMsec.ToString() + ", " + this._TransitionSpeedMsec.ToString() + ", '" + this.ParseEfect(this._Effect) + "');";
        }

        /// <summary>
        /// Display Error Messages
        /// </summary>
        /// <param name="writer"></param>
        private void DisplayErrorMessage(HtmlTextWriter writer) {
            writer.Write("<span style=\"color:Red;\">Error loading webpart: " + this._ErrorMessageLog.Replace(System.Environment.NewLine, "<br />").ToString() + "</span>");
        }

        /// <summary>
        /// Load the Controls
        /// </summary>
        private void LoadControls() {

            // Rotator Panel
            this.uiPnlRotatorContainer.ID = "RssFeedContainer";
            this.uiPnlRotatorContainer.CssClass = "rssrotatorcontainer";
            this.uiPnlRotatorContainer.EnableViewState = false;

            // Add the DataSource to the page
            this.uiPnlRotatorContainer.Controls.Add(uiXmlDataSource);

            // Add Dynamic messages 
            this.uiLitDynamicMessage.Text = "Loading ...";
            this.uiPnlRotatorContainer.Controls.Add(uiLitDynamicMessage);         

            // Add Rotator Panel to page        
            this.Controls.Add(uiPnlRotatorContainer);
        }

        /// <summary>
        /// Databind Controls
        /// </summary>
        private void DatabindControls() {

            // Set XML Data Source parameters
            this.uiXmlDataSource.ID = "uiXmlDataSource";
            this.uiXmlDataSource.XPath = string.Format("rss/channel/item[position()<={0}]", this._TotalFeedCount.ToString());
            this.uiXmlDataSource.EnableViewState = false;
            this.uiXmlDataSource.EnableCaching = this._DataCache > 0;
            this.uiXmlDataSource.CacheDuration = this._DataCache;
            this.uiXmlDataSource.CacheExpirationPolicy = DataSourceCacheExpiry.Absolute;
            this.uiXmlDataSource.CacheKeyDependency = this._RssFeedUrl;

            //get rss data (xml)
            // Load the Document from the URL Response only if not cached data
            if (string.IsNullOrEmpty(this.uiXmlDataSource.Data)) {
                this.uiXmlDataSource.Data = this.GetResponse();
            }
            this.uiXmlDataSource.DataBind();            

            //serialize this webpart
            XmlDocument serializedWP = new XmlDocument();
            serializedWP.LoadXml(SerializeThis());

            //append rss data to serialized webpart to get final XML
            //this is the xml that will be transformed by the provided XSL
            this._RssXMLDocument = this.uiXmlDataSource.GetXmlDocument();
            foreach(XmlNode extraNode in this._RssXMLDocument.SelectNodes(string.Format("rss/channel/item[position()>{0}]", this._TotalFeedCount.ToString()))){
                this._RssXMLDocument.SelectSingleNode("rss/channel").RemoveChild(extraNode);
            }
            XmlNode rss = serializedWP.ImportNode(this._RssXMLDocument.SelectSingleNode("rss"), true);
            serializedWP.FirstChild.AppendChild(rss);

            //set property for internal consumption
            this._Xml = serializedWP.OuterXml;

            //remove "Loading ..." text
            this.uiPnlRotatorContainer.Controls.Remove(uiLitDynamicMessage);

            //add transformed html to webpart
            Literal uiLit = new Literal();
            if (!string.IsNullOrEmpty(this._Xsl)) {
                uiLit.Text = GetTransformResults(this._Xsl, this._Xml);
            } else {
                uiLit.Text = GetXmlForBrowser(this._Xml);
            }
            this.uiPnlRotatorContainer.Controls.Add(uiLit);
        }

        /// <summary>
        /// Get the Web Response
        /// </summary>
        /// <returns></returns>
        private string GetResponse() {
            HttpWebResponse webResponse = null;
            StreamReader xmlReader = null;
            try {
                // Get the Rss Uri
                string uri;
                if (this._LinkRelativeURL) {                    
                    uri = System.Web.HttpContext.Current.Request.Url.AbsoluteUri;
                    string domain = uri.Replace(System.Web.HttpContext.Current.Request.Url.AbsolutePath, "");
                    uri = string.Concat(domain, this._RssFeedUrl);
                } else {
                    uri = this._RssFeedUrl;
                }
                Uri rssUri = new Uri(uri);
                HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(rssUri);

                // Handle SSL 
                ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
                HttpRequestCachePolicy policy = new HttpRequestCachePolicy(HttpRequestCacheLevel.BypassCache);
                webRequest.CachePolicy = policy;

                // Credentials
                webRequest.PreAuthenticate = true;
                webRequest.Credentials = CredentialCache.DefaultNetworkCredentials;
                webRequest.ContentType = "text/html";
                webRequest.KeepAlive = false;

                // Get the Response                
                webResponse = (HttpWebResponse)webRequest.GetResponse();

                // If successful then return the XML
                if (webResponse.StatusCode == System.Net.HttpStatusCode.OK) {
                    xmlReader = new StreamReader(webRequest.GetResponse().GetResponseStream()); ;
                    return xmlReader.ReadToEnd();
                }
            } catch (Exception ex) {
                throw new ApplicationException(string.Format("The Rss Feed: '{0}' could not be acessed.", this._RssFeedUrl));
            } finally {
                if (xmlReader != null) {
                    xmlReader.Close();
                }
                if (webResponse != null) {
                    webResponse.Close();
                }
            }

            return string.Empty;
        }

        /// <summary>
        /// Parse Effect
        /// </summary>
        /// <param name="effect"></param>
        /// <returns></returns>
        private string ParseEfect(EffectOptions effect) {
            switch (effect) {
                case EffectOptions.Cover:
                    return "cover";
                case EffectOptions.Fade:
                    return "fade";
                case EffectOptions.Scroll_Horz:
                    return "scrollHorz";
                case EffectOptions.Scroll_Vert:
                    return "scrollVert";
                case EffectOptions.Scroll_Up:
                    return "scrollUp";
                case EffectOptions.Scroll_Down:
                    return "scrollDown";
                case EffectOptions.Scroll_Left:
                    return "scrollLeft";
                case EffectOptions.Scroll_Right:
                    return "scrollRight";
                case EffectOptions.Uncover:
                    return "uncover";
                default:
                    return effect.ToString();
            }
        }


        /// <summary>
        /// Trim left the String
        /// </summary>
        /// <param name="StringToTrim">Very Long Name that needs to be shorter</param>
        /// <param name="NumOfCharacters">20</param>
        /// <param name="FindSpace">true</param>
        /// <param name="MarkConcatenation">" ..."</param>
        /// <returns></returns>
        private string TrimLeft(string StringToTrim, int NumOfCharacters, bool FindSpace, string MarkConcatenation) {
            if (StringToTrim.Length > NumOfCharacters) {
                string _retVal = StringToTrim.Substring(0, NumOfCharacters);
                if (FindSpace) {
                    int lastSpace = _retVal.LastIndexOf(" ");
                    if (lastSpace > 0) {
                        _retVal = _retVal.Substring(0, lastSpace) + MarkConcatenation;
                    }
                    return _retVal;
                } else {
                    return _retVal;
                }
            } else {
                return StringToTrim;
            }
        }

        /// <summary>
        /// Convert HTML to Text 
        /// </summary>
        /// <param name="HtmlContent"></param>
        /// <returns></returns>
        public string HtmlToText(string HtmlContent) {
            if (HtmlContent == null) {
                return "";
            }
            Regex _reg = new Regex(@"(<){1}[^>]*(>){1}");
            return _reg.Replace(HtmlContent, "").Replace("&nbsp;", " ");
        }      

        #endregion Private Methods      

        #region Classes

        /// <summary>
        /// Syndicate Feed Class
        /// </summary>
        public class SyndicationFeed {
            public SyndicationFeed() { this.Items = new List<SyndicationItem>(); }

            public string Title { get; set; }
            public string Link { get; set; }
            public string Description { get; set; }
            public string Copyright { get; set; }
            public DateTime PublishDate { get; set; }
            public List<SyndicationItem> Items { get; set; }
           
        }

        /// <summary>
        /// Syndicate Feed item class
        /// </summary>
        public class SyndicationItem {
            public string Title { get; set; }
            public string Link { get; set; }
            public string Description { get; set; }
            public DateTime PublishDate { get; set; }
        }

        [XmlRoot(ElementName="WebcodaRssRotator")]
        [Serializable]
        public struct WebPartProperties {
            public string RssFeedUrl;
            public int TotalFeedCount;
            public int NoItemsDisplay;
            public int DescriptionSize;
            public bool LinkInNewWindow;
            public bool LinkRelativeUrl;
            public bool DisplayDate;
            public bool DisplayHeaderLink;
            //public string ItemCssClass;
        }
        #endregion Classes

        #region XSL Transform

        private string SerializeThis() {


            WebPartProperties wpp = new WebPartProperties();
            wpp.RssFeedUrl = this._RssFeedUrl;
            wpp.TotalFeedCount = this._TotalFeedCount;
            wpp.NoItemsDisplay = this._NoItemsDisplay;
            wpp.DescriptionSize = this._DescriptionSize;
            wpp.LinkInNewWindow = this._LinkInNewWindow;
            wpp.LinkRelativeUrl = this._LinkRelativeURL;
            wpp.DisplayDate = this._DisplayDate;
            wpp.DisplayHeaderLink = this._DisplayHeaderLink;
            //wpp.ItemCssClass = this._ItemCssClass;


            StringBuilder sb = new StringBuilder();

            //Serialize this webpart to make properties available to XSL
            XmlSerializer serializer = new XmlSerializer(typeof(WebPartProperties));

            XmlWriterSettings xmlSettings = new XmlWriterSettings();
            xmlSettings.OmitXmlDeclaration = true;
            //xmlSettings.ConformanceLevel = ConformanceLevel.Fragment;
            StringWriter sw = new StringWriter();
            using (XmlWriter xmlWriter = XmlWriter.Create(sw, xmlSettings)) {
                //serializer.Serialize(xmlWriter, wpp);
                serializer.Serialize(xmlWriter, wpp);
            }
            return sw.ToString();
        }

        /// <summary>
        /// Transform XML using specified XSL stylesheet
        /// </summary>
        /// <param name="xsl"></param>
        /// <param name="responseStream"></param>
        /// <returns></returns>
        public static string GetTransformResults(string xsl, string xml) {

            XmlUrlResolver resolver = new XmlUrlResolver();
            resolver.Credentials = CredentialCache.DefaultCredentials;

            //when linking to a xsl stylesheet use the following line
            //XmlTextReader stylesheet = new XmlTextReader(SPContext.Current.Site.Url + xslLink); 
            XmlTextReader stylesheet = new XmlTextReader(new StringReader(xsl));
            stylesheet.XmlResolver = resolver;

            XslCompiledTransform transform = new XslCompiledTransform();
            transform.Load(stylesheet);

            StringBuilder sb = new StringBuilder();
            TextWriter writer = new StringWriter(sb);
            StringReader sr = new StringReader(xml);

            using (XmlReader reader = XmlTextReader.Create(sr)) {
                XmlTextWriter results = new XmlTextWriter(writer);
                transform.Transform(reader, results);
                reader.Close();
            }

            return sb.ToString();

        }

        /// <summary>
        /// Transform Raw XML using default XSL stylesheet
        /// </summary>
        /// <param name="memoryStream"></param>
        /// <returns></returns>
        public static string GetXmlForBrowser(string xml) {

            //string xsl = "<xsl:stylesheet version=\"1.0\" exclude-result-prefixes=\"x d xsl msxsl cmswrt\" xmlns:x=\"http://www.w3.org/2001/XMLSchema\" xmlns:d=\"http://schemas.microsoft.com/sharepoint/dsp\" xmlns:cmswrt=\"http://schemas.microsoft.com/WebParts/v3/Publishing/runtime\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:msxsl=\"urn:schemas-microsoft-com:xslt\">" +
            //        "<xsl:template match=\"/\"><textarea rows=\"20\" cols=\"100\"><xsl:apply-templates/></textarea>" +
            //        "</xsl:template><xsl:template match=\"node()|@*\"><!-- Copy the current node --><xsl:copy>" +
            //        "<!-- Including any child nodes it has and any attributes --><xsl:apply-templates select=\"node()|@*\"/>" +
            //        "</xsl:copy></xsl:template></xsl:stylesheet>";

            //return GetTransformResults(xsl, xml);
            return GetTransformResults(ReadXslFileContents(), xml);
        }

        /// <summary>
        /// Read contents of the XSL Stylesheet that is deployed with the webpart.
        /// </summary>
        /// <returns></returns>
        public static string ReadXslFileContents() {

            string pathToXslt = "";

            SPSecurity.RunWithElevatedPrivileges(delegate() {
                pathToXslt = HttpContext.Current.Server.MapPath("/_layouts/webcoda.rssrotator/RssRotator.xslt");
            });

            if (string.IsNullOrEmpty(pathToXslt) == false) {
                TextReader tr = new StreamReader(pathToXslt);
                return tr.ReadToEnd();
            } else {
                return string.Empty;
            }
        }

        #endregion
    }
}
